
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(executor) {
    try {
      executor(this.revolse, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  status = PENDING
  value = undefined
  reason = undefined
  successCallBack = []
  failCallBack = []

  revolse = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    this.successCallBack.map(cb => cb(this.value))
  }

  reject = (reason) => {
    if (this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    this.failCallBack.map(cb => cb(this.reason))

  }

  then (successCallBack, failCallBack) {
    // then 方法的参数可以非必传，如过参数为空， 给一个默认函数 
    successCallBack = typeof successCallBack === 'function' ? successCallBack : v => v
    failCallBack = typeof failCallBack === 'function' ? failCallBack : r => {
      throw r
    }
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => { // 为了保证promise2  的存在
          const x = successCallBack(this.value)
          // resolve(x)
          resolvePromise(promise2, x, resolve, reject)
        }, 0)

      } else if (this.status === REJECTED) {
        setTimeout(() => { // 为了保证promise2  的存在
          failCallBack(this.reason)
          reject(this.reason)
        }, 0)

      } else {
        // 等待状态

        // 异步，需要将callback 缓存

        // 如果successCallBack 返回的是promise 后面的.then 方法会拿不到，需要等promise 的状态改变， 写成下面的方法
        // _this.successCallBack.push(successCallBack)
        // _this.failCallBack.push(failCallBack)

        // 
        this.successCallBack.push(() => { // 多个then 返回的promise ，确保callback 可以调用
          const x = successCallBack(this.value)
          // resolve(x)
          resolvePromise(promise2, x, resolve, reject)
          // setTimeout(() => { // 为了保证promise2  的存在

          // }, 0)
        })
        this.failCallBack.push(() => { // 多个then 返回的promise ，确保callback 可以调用
          const x = failCallBack(this.reason)
          // resolve(x)
          resolvePromise(promise2, x, resolve, reject)
          // setTimeout(() => { // 为了保证promise2  的存在
          // }, 0)
        })
      }

    })

    return promise2
  }

  static all (promises) {
    const value = []
    let count = 0
    return new Promise((resolve, reject) => {
      promises.forEach((promise, idx) => {
        Promise.resolve(promise).then(res => {
          count++
          value[idx] = res
          if (count === promises.length) {
            console.log(111111111);
            resolve(value)
          }
        }, err => {
          reject(err)
        })
      })
    })
  }

  static allSelected (promises) {
    const value = []
    let count = 0
    return new Promise((resolve, reject) => {
      promises.forEach((promise, idx) => {
        Promise.resolve(promise).then(res => {
          value[idx] = {
            value: res,
            reason: null
          }
        }, err => {
          value[idx] = {
            value: null,
            reason: err
          }
        }).finally(() => {
          count++
          if (count === promises.length) {
            resolve(value)
          }
        })
      })
    })
  }

}

function resolvePromise (promise2, x, resolve, reject) {
  // 判断x 是否是promise
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

// module.exports = MyPromise

function maxRequest (fn, maxNum) {
  return new Promise((resolve, reject) => {
    Promise.resolve(fn()).then(value => {
      revolse(value)
    }).catch(err => {
      if (!!maxNum) {
        return maxRequest(fn, maxNum - 1)
      } else {
        reject(err)
      }
    })
  })
}

function test () {
  console.log(1);
  return Promise.reject('123')
}

maxRequest(test, 3)

function test2 () {
  return Promise.resolve('123')
}
console.log(MyPromise.all);
MyPromise.all([test2(), test(), test2()]).then(res => {
  console.log(res);
}, err => {
  console.log(err, 999);
})

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];

Promise.allSettled(promises).
  then((results) => console.log(results));
